/*
	MIDAS Context Based Routing
	cbr_packet.cc
	Copyright (c) 2007 Radoslaw Olgierd Schoeneich
	Copyright (c) 2007 Bartlomiej Zolnierkiewicz
	MEAG IT PW
*/

#include <iostream>
#include <stdio.h>
#include <algorithm>

#include "scheduler.h"
#include "packet.h"

#include "cbr_packet.h"
#include "serializer.h"
#include "cbr_debug.h"

// size of a packet's header
int Packet::hdrlen_ = sizeof(struct hdr_cmn)	// from packet.c
		    + sizeof(struct hdr_ip)
		    + sizeof(struct hdr_cbr);

// static offset of common header
int hdr_cmn::offset_ = 0;			// from packet.c

int hdr_ip::offset_ = sizeof(struct hdr_cmn);	// from ip.c

int hdr_cbr::offset_ = sizeof(struct hdr_cmn) + sizeof(struct hdr_ip);

// free list
Packet* Packet::free_;				// from packet.c

int32_t cbr_Packet::maxSize = 1500;
int32_t cbr_DataPacket::maxPayload = 1500 - sizeof(hdr_cbr) - 12;

using namespace std;

/**
 * HELLO packet constructor.
 *
 * @param src the source node ID
 * @param list list of CBR triples
 *
 * Used for generating new HELLO packet.
 * \verbatim HELLO packet format:
 *
 * struct hdr_cbr cmn_hdr	common header
 * u_int8_t int_count		number of internal concepts
 * <stringz> concept		\
 * u_int8_t metric		|  int_count x context routing triple
 * u_int32_t source_ID		/
 * u_int8_t ext_count		number of external concepts
 * <stringz> concept		\
 * u_int8_t metric		|  ext_count x context routing triple
 * u_int32_t source_ID		/ \endverbatim
 */

cbr_HelloPacket::cbr_HelloPacket(cbr_ID src, const cbr_TriplesList& list)
{
	cbr_TriplesList::const_iterator i;

	this->pkt_ = Packet::alloc();
	this->src = src;
	this->dest = IP_BROADCAST;

	this->external.assign(list.begin(), list.end());
	this->internal.clear();

	hdr_cbr* generic_hdr = hdr_cbr::access(this->pkt_);
	generic_hdr->type = CBRTYPE_HELLO;
	generic_hdr->src = (u_int32_t) src;

	PacketData *data = new PacketData(cbr_Packet::maxSize - sizeof(hdr_cbr));
	this->pkt_->setdata(data);
//	u_int8_t* start = (u_int8_t*) generic_hdr + sizeof(hdr_cbr);
	u_int8_t* start = (u_int8_t *)data->data();
	serializer ser(start, cbr_Packet::maxSize - sizeof(hdr_cbr));

	ser << (u_int8_t) internal.size();
//	cbr_debug("%s int size: %u\n", __FUNCTION__, internal.size());
	for (i = internal.begin(); i != internal.end(); i++) {
//		cbr_Triple_dump(*i); //
		ser << (*i)->getConcept();
		ser << (u_int8_t) (*i)->getMetric();
		ser << (u_int32_t) (*i)->getSource();
	}

	ser << (u_int8_t) external.size();
//	cbr_debug("%s ext size: %u\n", __FUNCTION__, external.size());
	for (i = external.begin(); i != external.end(); i++) {
//		cbr_Triple_dump(*i); //
		ser << (*i)->getConcept();
		ser << (u_int8_t) (*i)->getMetric();
		ser << (u_int32_t) (*i)->getSource();
	}

	this->size = generic_hdr->size = sizeof(hdr_cbr) + ser.get_pos();
/*
	size -= sizeof(hdr_cbr);
	cbr_debug("packet contents (size: %d):", size);
	int c = 0;
	for (start = (u_int8_t *)data->data(); c < size; c++) {
		if ((c % 16) == 0)
			cbr_debug("\n   ");
		cbr_debug(" %02x", start[c]);
	}
	c = 0;
	for (start = (u_int8_t *)data->data(); c < size; c++) {
		if ((c % 16) == 0)
			cbr_debug("\n   ");
		cbr_debug("%c", (isascii(start[c]) && start[c] != '\0') ?
					start[c] : '.');
	}
	cbr_debug("\n");
*/
}

/**
 * HELLO packet constructor.
 *
 * @param pkt generic packet
 *
 * Constructs HELLO packet from a generic packet.
 *
 * Used on reception of HELLO packet.
 */

cbr_HelloPacket::cbr_HelloPacket(Packet *pkt)
{
	hdr_cbr* generic_hdr = hdr_cbr::access(pkt);
	cbr_Triple *triple;
	std::string str;
	u_int8_t metric, cnt;
	u_int32_t src;

	this->pkt_ = pkt;
	this->src = (cbr_ID) generic_hdr->src;
	this->dest = IP_BROADCAST;
	this->size = generic_hdr->size;

	PacketData *data = (PacketData *)pkt->userdata();
//	u_int8_t* start = (u_int8_t*) generic_hdr + sizeof(hdr_cbr);
	u_int8_t* start = (u_int8_t *)data->data();
	serializer ser(start, this->size - sizeof(hdr_cbr));

	ser >> cnt;
	while (cnt-- > 0) {
		ser >> str >> metric >> src;
/*
		cbr_debug("got int triple: '%s' @ node %u is %u hop(s) away\n",
					str.c_str(), src, (unsigned) metric);
 */
		triple = new cbr_Triple(str, (int) metric, (cbr_ID) src);
		internal.push_back(triple);
	}

	ser >> cnt;
	while (cnt-- > 0) {
		ser >> str >> metric >> src;
/*
		cbr_debug("got ext triple: '%s' @ node %u is %u hop(s) away\n",
					str.c_str(), src, (unsigned) metric);
 */
		triple = new cbr_Triple(str, (int) metric, (cbr_ID) src);
		external.push_back(triple);
	}

	size = ser.get_pos();
}

/**
 * HELLO packet destructor.
 */

cbr_HelloPacket::~cbr_HelloPacket()
{
	cbr_TriplesList::iterator i, j;

	for (i = internal.begin(); i != internal.end(); ) {
		j = i;
		i++;
		delete (*j);
	}
	for (i = external.begin(); i != external.end(); ) {
		j = i;
		i++;
		delete (*j);
	}
}

/**
 * Get HELLO packet triples.
 *
 * @param internal list of internal concepts
 * @param external list of external concepts
 *
 */

void cbr_HelloPacket::getTriples(cbr_TriplesList& internal,
				 cbr_TriplesList& external) const
{
	internal.assign(this->internal.begin(), this->internal.end());
	external.assign(this->external.begin(), this->external.end());
}

/**
 * DATA packet constructor.
 *
 * @param pkt generic packet
 *
 * Constructs DATA packet from a generic packet.
 *
 * Used on reception of DATA packet.
 * \verbatim
 * DATA packet format:
 *
 * struct hdr_cbr cmn_hdr	common header
 * u_int32_t dest		destination node ID
 * u_int32_t transaction_ID	transaction (message) ID
 * u_int32_t offset		offset in message
 * u_int8_t data[]		payload \endverbatim
 */

cbr_DataPacket::cbr_DataPacket(Packet *pkt)
{
	hdr_cbr* generic_hdr = hdr_cbr::access(pkt);

	this->pkt_ = pkt;
	this->src = (cbr_ID) generic_hdr->src;
	this->type = generic_hdr->type & CBRTYPE_MASK;
	this->size = generic_hdr->size;

	PacketData *data = (PacketData *)pkt->userdata();
//	u_int8_t* start = (u_int8_t*) generic_hdr + sizeof(hdr_cbr);
	u_int8_t* start = (u_int8_t *)data->data();
	serializer ser(start, this->size - sizeof(hdr_cbr));

	ser >> this->dest >> this->transactionID >> this->offset;
	this->payloadLength = this->size - sizeof(hdr_cbr) - ser.get_pos();
	this->payload = new char[this->payloadLength];
	std::copy(start + ser.get_pos(),
		  (u_int8_t*) generic_hdr + this->size,
		  this->payload);
}

/**
 * DATA packet constructor.
 *
 * @param src the source node ID
 * @param dest the destination node ID
 * @param trID the transaction ID
 * @param payload payload
 * @param offset offset in the message
 * @param length payload length
 *
 * Used for generating new DATA packet.
 */

cbr_DataPacket::cbr_DataPacket(cbr_ID src, cbr_ID dest, u_int32_t trID,
			const char *payload, int32_t offset, int32_t length)
{
	this->pkt_ = Packet::alloc();
	this->src = src;
	this->type = CBRTYPE_DATA;
	this->dest = dest;

	this->transactionID = trID;
	this->offset = offset;

	this->payloadLength = length;
	this->payload = new char[length];
	std::copy(payload, payload + length, this->payload);

	hdr_cbr* generic_hdr = hdr_cbr::access(this->pkt_);
	generic_hdr->type = CBRTYPE_DATA;
	generic_hdr->src = (u_int32_t) src;

	PacketData *data = new PacketData(this->size - sizeof(hdr_cbr));
	this->pkt_->setdata(data);
//	u_int8_t* start = (u_int8_t*) generic_hdr + sizeof(hdr_cbr);
	u_int8_t* start = (u_int8_t *)data->data();
	serializer ser(start, cbr_Packet::maxSize - sizeof(hdr_cbr));

	ser << this->dest << this->transactionID << this->offset;
	std::copy(payload, payload + length, start + ser.get_pos());
	this->size = sizeof(hdr_cbr) + ser.get_pos() + payloadLength;
	generic_hdr->size = this->size;
}

/**
 * DATA packet destructor.
 */

cbr_DataPacket::~cbr_DataPacket()
{
	if (this->payload)
		delete[] this->payload;
}

